﻿using System;
using System.IO;
using System.Text;
using System.Windows;
using Adjunct.Framework.MSBuild;
using Adjunct.Framework.NuGet;
using Adjunct.Framework.Nuspec;

namespace Adjunct.Generator.Framework
{
	public static class Foundry
	{
		public static void Mold(NuspecManifest nuspec, GenerateOptions generateOptions)
		{
			NuspecMetadata metadata = nuspec.Metadata;
			string name = metadata.Title;

			string solutionDir = Path.Combine(generateOptions.GenerateToPath, name).EnsureEndsWith(@"\");
			if (Directory.Exists(solutionDir))
			{
				//force override flag?
				string message = string.Format("Adjunct [{0}] exists, overwrite? [y/n]", name);
				if (MessageBox.Show(message, "Overwrite Extension?", MessageBoxButton.YesNo) == MessageBoxResult.No)
				{
					return;
				}
			}
			//prepare directories
			Directory.CreateDirectory(solutionDir);
			Directory.SetCurrentDirectory(solutionDir);
			Directory.CreateDirectory(@".\Source\Properties");
			Directory.CreateDirectory(@".\Tests");

			string projectDir = Path.Combine(solutionDir, "Source").EnsureEndsWith(@"\");
			string toolsVersion = Get.GeneratorVersion();

			if (metadata.Tags == null)
			{
				metadata.Tags = string.Empty;
			}

			//Source.nuspec
			if (metadata.Id.StartsWith("adjunct-"))
			{
				if (!metadata.Tags.Contains("Adjunct"))
				{
					metadata.Tags = "Adjunct " + metadata.Tags;
				}
			}
			nuspec.Save(string.Format(@".\Source\{0}.nuspec", metadata.Title));

			//Tests.nuspec
			NuspecManifest testNuspec = nuspec.Clone();
			testNuspec.Metadata.Id += ".Tests";
			testNuspec.Metadata.Title += ".Tests";
			testNuspec.Metadata.Description = "XUnit tests for " + metadata.Id;
			testNuspec.Metadata.Dependencies.Add(metadata.Id, metadata.Version);
			testNuspec.Metadata.Dependencies.Add("xunit");
			testNuspec.Metadata.Tags = metadata.Tags.Replace("Adjunct ", "Adjunct-Tests ");
			testNuspec.Save(string.Format(@".\Tests\{0}.Tests.nuspec", metadata.Title));

			//handle license file
			string licenseItemContent = string.Empty;
			if (!string.IsNullOrWhiteSpace(generateOptions.LocalLicensePath))
			{
				string licenseFilename = Path.GetFileName(generateOptions.LocalLicensePath);
				if (generateOptions.CopyLicenseLocal)
				{
					File.Copy(generateOptions.LocalLicensePath, Path.Combine(solutionDir, licenseFilename), true);
					licenseItemContent = new ProjectItem("Content", string.Format(@"..\{0}", licenseFilename), licenseFilename).ToXmlString();
				}
				else
				{
					licenseItemContent =
						new ProjectItem("Content", BuildRelativePath(projectDir, generateOptions.LocalLicensePath), licenseFilename).ToXmlString();
				}
			}

			string relativeDeployPath = BuildRelativePath(projectDir, generateOptions.LocalDeployPath);

			//setup template engine
			TemplatedTextService templating = new TemplatedTextService();
			templating.AddReplacement("ToolsVersion", toolsVersion);
			templating.AddReplacement("ExtensionName", name);
			templating.AddReplacement("LicenseItem", licenseItemContent);
			templating.AddReplacement("PackagesRelativeToSolution", BuildRelativePath(solutionDir, generateOptions.PackagesPath));
			templating.AddReplacement("PackagesRelativeToProject", BuildRelativePath(projectDir, generateOptions.PackagesPath));
			templating.AddReplacement("XunitVersion", PackageDirectory.GetMaxInstalledVersion(generateOptions.PackagesPath, "xunit").Value);
			templating.AddReplacement("DeployPath",
			                          string.Format(@"$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\{0}'))", relativeDeployPath));

			//solution items
			ProcessResource(templating, "Template.Template.sln", string.Format(@".\{0}.sln", name));
			ProcessResource(templating, "Template.nuget.config", @".\nuget.config");
			ProcessResource(templating, "Template.Build.bat", @".\Build.bat");
			//source items
			ProcessResource(templating, "Template.Source.Source.csproj", string.Format(@".\Source\{0}.csproj", name));
			ProcessResource(templating, "Template.Source.Source.csproj.ReSharper", string.Format(@".\Source\{0}.csproj.ReSharper", name));
			ProcessResource(templating, "Template.Source.AssemblyInfo.cs", @".\Source\Properties\AssemblyInfo.cs");
			ProcessResource(templating, "Template.Source.packages.config", @".\Source\packages.config");
			//test items
			ProcessResource(templating, "Template.Tests.Tests.csproj", string.Format(@".\Tests\{0}.Tests.csproj", name));
			ProcessResource(templating, "Template.Tests.Tests.csproj.ReSharper", string.Format(@".\Tests\{0}.Tests.csproj.ReSharper", name));
			ProcessResource(templating, "Template.Tests.packages.config", @".\Tests\packages.config");

			//deploy version.targets from Build Package
			string versionTargetsPath = Path.Combine(generateOptions.PackagesPath,
			                                         "Adjunct.Build." + toolsVersion,
			                                         "content",
			                                         "Adjunct.Version.targets");
			File.Copy(versionTargetsPath, Path.Combine(solutionDir, "Source", "Adjunct.Version.targets"), true);
			File.Copy(versionTargetsPath, Path.Combine(solutionDir, "Tests", "Adjunct.Version.targets"), true);

			//update repositories.config
			string repositoriesConfigPath = Path.Combine(generateOptions.PackagesPath, "repositories.config");
			RepositoriesConfig repositoriesConfig = RepositoriesConfig.Load(repositoriesConfigPath);
			repositoriesConfig.Add(BuildRelativePath(generateOptions.PackagesPath.EnsureEndsWith(@"\"),
			                                         Path.Combine(solutionDir, @"Source\packages.config")));
			repositoriesConfig.Add(BuildRelativePath(generateOptions.PackagesPath.EnsureEndsWith(@"\"),
			                                         Path.Combine(solutionDir, @"Tests\packages.config")));
			repositoriesConfig.Save(repositoriesConfigPath);

			MessageBox.Show(string.Format("{0} created.", name));
		}

		// remember to ensure that a directory ends in \ before calling this
		private static string BuildRelativePath(string fromHere, string toHere)
		{
			Uri fromUri = new Uri(fromHere);
			Uri toUri = new Uri(toHere);
			Uri relativeUri = fromUri.MakeRelativeUri(toUri);
			return Uri.UnescapeDataString(relativeUri.ToString()).Replace('/', Path.DirectorySeparatorChar);
		}

		private static void ProcessResource(TemplatedTextService templating, string resourceName, string fileName)
		{
			string content = PackedResources.GetString(resourceName);
			content = templating.Process(content);
			using (FileStream fileStream = File.Create(fileName))
			{
				using (StreamWriter writer = new StreamWriter(fileStream))
				{
					writer.Write(content);
				}
			}
		}
	}
}